#! /usr/bin/env node


import minimist from 'minimist'
import {blue,
  cyan,
  green,
  lightBlue,
  lightGreen,
  lightRed,
  magenta,
  red,
  reset,
  yellow,} from 'kolorist'
import prompts from 'prompts'
import fs from 'fs-extra'
import path from 'path'
import { fileURLToPath } from 'url'
const parseArg=minimist(process.argv.slice(2))
//命令行获取的文件目录，和指定模板
const argTargetDir=parseArg._[0]
const argTempate=parseArg.template

//默认文件目录
const defaultTargetDir='vite-project'

// 最终获取到的文件目录
let targetDir=argTargetDir||defaultTargetDir //初始化

const getProjectName = () =>
    targetDir === '.' ? path.basename(path.resolve()) : targetDir

//模板可选项
const frameWork=[{
    name: 'vue',
    display: 'Vue',
    color: green,
    variants: [
      {
        name: 'vue-ts',
        display: 'TypeScript',
        color: blue,
      },
      {
        name: 'vue',
        display: 'JavaScript',
        color: yellow,
      },
      {
        name: 'custom-create-vue',
        display: 'Customize with create-vue ↗',
        color: green,
        customCommand: 'npm create vue@latest TARGET_DIR',
      }
    ],
  },
  {
    name: 'react',
    display: 'React',
    color: cyan,
    variants: [
      {
        name: 'react-ts',
        display: 'TypeScript',
        color: blue,
      },
      {
        name: 'react-swc-ts',
        display: 'TypeScript + SWC',
        color: blue,
      },
      {
        name: 'react',
        display: 'JavaScript',
        color: yellow,
      },
      {
        name: 'react-swc',
        display: 'JavaScript + SWC',
        color: yellow,
      },
    ],
    }]
//模板名称数组
const TEMPLATES = frameWork.map(
  (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name],
).reduce((a, b) => a.concat(b), [])
// console.log('TEMPLATES---',TEMPLATES)
  async function init() {
    
  const result=await prompts([
      { // 
          type: argTargetDir ? null : 'text',//type 如果为null那就不进行此操作
          name: 'projectName',
          message: reset('Project name:'),
          initial: defaultTargetDir,
          onState: (state) => {
            targetDir = formatTargetDir(state.value) || defaultTargetDir
          }
      },
      {
          type:()=>!fs.pathExistsSync(targetDir)||isEmpty(targetDir)?null:'select',//判断当前目录下是否有文件。若存在则选择以下问询提供的操作方式
          name:'overwrite',
          message:()=>`目标文件${targetDir}不为空，请选择如何处理`,
          initial:0,
          choices:[
              {
                  title:'删除已存在的文件并继续',
                  value:'yes'
              },
              {
                  title:'取消操作',
                  value:'no'
              },{
                  title:'忽略已存在的文件并继续',
                  value:'ignore'
              }
          ]
      },
      {
          type:(prev, values)=>{//prev是来自上一个提示的值，values是迄今为止收集的所有值的响应对象
              if(values.overwrite==='no'){
                  throw new Error(red('✖') + ' 取消操作')
              }
              return null
          },
          name:'overwriteChecker'
      },
      {
          type: () => (isValidPackageName(getProjectName()) ? null : 'text'),
          name: 'packageName',
          message: reset('Package name:'),
          initial: () => toValidPackageName(getProjectName()),
          validate: (dir) =>
            isValidPackageName(dir) || 'Invalid package.json name',
      },
      {
          /*判断命令行是否有设置模板参数，如果没有则根据提示的选择项进行选择
              framework存的是vue或react，具体vue-ts/vue-js  或者 react-ts/react-js 在下一步问询中
          */
          type: () => (argTempate && TEMPLATES.includes(argTempate) ? 'null' : 'select'),
          name: 'framework',
          message:(argTempate==='string'&&(!TEMPLATES.includes(argTempate)))?reset(`${argTempate}是不支持的，请从一下模板中选择`):reset(`请选择一个模板`),
          initial: 0,
          choices: frameWork.map(el => {
              const frameWorkColor = el.color
              return {
                  title: frameWorkColor(el.display || el.name),
                  value:el
              }
          })
      },
      { // 根据上一步指令的结果，选择指定的模板版本
          type: (prev) => {
              // console.log('prev----',prev,prev.variants)
              if (prev && prev.variants) {
                  return 'select'
              } else {
                  return null
              }
          } ,
          name: 'variant',
          message: '选择一个具体模板类型',
          initial: 0,
          choices: (prev) => prev.variants.map(el => {
                  const variantColor = el.color
                  return {
                      title: variantColor(el.display || el.name),
                      value:el.name
                  }
              })
      }
  ])
    console.log(result)
  //问询后的所有结果
    const { framework, overwrite, packageName, variant } = result
    const cwd = process.cwd()
    const root = path.join(cwd, targetDir)
    
    //创建项目根目录
    if (overwrite === 'yes') {//删除目录下的所有文件
      fs.emptyDir(root)
    } else if (!fs.pathExistsSync(root)) {//创建目录
      fs.ensureDirSync(root)
    }


    /*
      userAgent获取到的参数格式：
      npm/6.14.5 node/v14.17.1 win32 x64
      yarn/1.22.10 npm/? node/v14.17.1 win32 x64
      */
  console.log('npm_user_agent----',process.env.npm_config_user_agent)
  const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) // 获取包管理器信息
  const pkgManager = pkgInfo ? pkgInfo.name : 'npm' // 包管理器名称
  const isYarn1 = pkgManager === 'yarn' && pkgInfo.version.startsWith('1.') // 包管理器版本是否为yarn的1.x版
  
  //获取选择的模板对应的文件夹
  let template = variant || framework.name || argTempate
  let isReactSwc = false
  if (template.includes('-swc')) {
    isReactSwc = true
    template = template.replace('-swc', '')
  }
  const templateDir = path.resolve(fileURLToPath(import.meta.url), '../', `template-${template}`) //获取模板路径
  
  const write = (file, content) => { //文件写入
      const targetPath = path.join(root, file)
      if (content) {
        fs.writeFileSync(targetPath, content)
      } else {
        copy(path.join(templateDir, file), targetPath)
      }
    }
  const files = fs.readdirSync(templateDir)
  for (const file of files.filter((f) => f !== 'package.json')) { //不复制package.json文件，此文件单独处理
    // write(file)
  }
    
  //单独复制package.json,因为模板的package.json中的name属性值需要改成项目名，而不是模板默认的那么值
    const pkg =fs.readJsonSync(path.join(templateDir,'package.json'),'utf-8') //读取json文件
    
    pkg.name = packageName || getProjectName()
    console.log('pkg---',JSON.stringify(pkg, null, 2) + '\n')
    write('package.json', JSON.stringify(pkg, null, 2) + '\n')

  // 如果输入的是react-swc-ts / react-swc
  if (isReactSwc) {
    setupReactSwc(root, template.endsWith('-ts'))
  }
    
  if (isReactSwc) {
    setupReactSwc(root, template.endsWith('-ts'))
  }

  const cdProjectName = path.relative(cwd, root)
  console.log(`\nDone. Now run:\n`)
  if (root !== cwd) {
    console.log(
      `  cd ${
        cdProjectName.includes(' ') ? `"${cdProjectName}"` : cdProjectName
      }`,
    )
  }
  switch (pkgManager) {
    case 'yarn':
      console.log('  yarn')
      console.log('  yarn dev')
      break
    default:
      console.log(`  ${pkgManager} install`)
      console.log(`  ${pkgManager} run dev`)
      break
  }
  console.log()
}

init()


function isEmpty(dir){//判断当前目录下是否有文件或文件夹
    const files=fs.readdirSync(dir)
    return files.length===0||(files.length==1&&files[0]=='.git')
}
function formatTargetDir(targetDir){
    return  targetDir?.trim().replace(/\/+$/g, '')
}
function isValidPackageName(projectName) {
    return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(
      projectName,
    )
  }
  
function toValidPackageName(projectName) {
  return projectName
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/^[._]/, '')
    .replace(/[^a-z\d\-~]+/g, '-')
}
function emptyDir(dir) { //清空文件夹下的文件
  if (!fs.pathExistsSync(dir)) {
    return
  }
  for (const file of fs.readdirSync(dir)) {
    if (file === '.git') {
      continue
    }
    fs.rmSync(path.resolve(dir, file), { recursive: true, force: true })
  }
}

function pkgFromUserAgent(userAgent) {//获取包管理信息
  if (!userAgent) return undefined
  const pkgSpec = userAgent.split(' ')[0] // npm/xxx  或 yarn/xxx   （xxx表示版本号）
  const pkgSpecArr = pkgSpec.split('/') // ['npm','xxx'] |  ['yarn','xxx'] 
    return {
      name: pkgSpecArr[0],
      version: pkgSpecArr[1],
    }
}


function copy(src, dest) {//复制功能
  const stat = fs.statSync(src)
  // console.log('state----',stat)
  if (stat.isDirectory()) {//当前目录是否为文件夹
    copyDir(src,dest) //复制文件夹
  } else {
    fs.copyFileSync(src,dest) // 直接复制文件
  }
}

function copyDir(srcDir, destDir) {
  fs.mkdirSync(destDir, { recursive: true }) //在目标路径先生成对应文件夹
  for (const file of fs.readdirSync(srcDir)) {
    const srcFile = path.resolve(srcDir, file)
    const destFile = path.resolve(destDir, file)
    copy(srcFile,destFile)
  }

}

function setupReactSwc(root, isTs) { // 替换react对应的包
  editFile(path.resolve(root, 'package.json'), (content) => {
    return content.replace(
      /"@vitejs\/plugin-react": ".+?"/,
      `"@vitejs/plugin-react-swc": "^3.5.0"`,
    )
  })
  editFile(
    path.resolve(root, `vite.config.${isTs ? 'ts' : 'js'}`),
    (content) => {
      return content.replace('@vitejs/plugin-react', '@vitejs/plugin-react-swc')
    },
  )
}

function editFile(file, callback) {
  const content = fs.readFileSync(file, 'utf-8')
  fs.writeFileSync(file, callback(content), 'utf-8')
}


